package com.common.util.redis;

import java.io.Serializable;
import java.util.Random;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisSentinelPool;

public class RedisLock implements Serializable{

	/** 
	* @Fields serialVersionUID :  
	*/ 
	private static final long serialVersionUID = 15659L;
	/** 加锁标志 */
	public static final String LOCKED = "TRUE";
	/** 秒与毫微秒的换算单位 1秒 = 1000000000纳秒 */
	public static final long MILLI_NANO_CONVERSION = 1000 * 1000000L;
	/** 默认超时时间（秒） */
	public static final long DEFAULT_TIME_OUT = 1;
	public static final Random RANDOM = new Random();
	/** 锁的超时时间（秒），过期删除 */
	public static final int EXPIRE = 3 * 60;

	public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public boolean isLocked() {
        return locked;
    }

    public void setLocked(boolean locked) {
        this.locked = locked;
    }

    /**
	 * 写数据用到的连接池
	 */
	private String key;
	// 锁状态标志
	private boolean locked = false;

	/**
	 * This creates a RedisLock
	 * @param key key
	 * @param shardedJedisPool 数据源
	 */
	public RedisLock(String key) {
		this.key = key + "_lock";
//		this.sentinelPool = sentinelPool;
//		this.jedis = this.sentinelPool.getResource();
	}


	public RedisLock() {
        super();
    }

    /**
	 * 加锁
	 * 应该以：
	 * lock();
	 * try {
	 * 		doSomething();
	 * } finally {
	 * 		unlock()；
	 * }
	 * 的方式调用
	 * @param timeout 超时时间
	 * @param expire 锁的超时时间（秒），过期删除
	 * @return 成功或失败标志
	 */
	public boolean lock(long timeout, int expire, JedisSentinelPool sentinelPool) {
		Jedis jedis = sentinelPool.getResource();
		long nano = System.nanoTime();
		timeout *= MILLI_NANO_CONVERSION;
		try {
			while ((System.nanoTime() - nano) < timeout) {
				if (jedis.setnx(this.key, LOCKED) == 1) {
					jedis.expire(this.key, expire);
					this.locked = true;
					return this.locked;
				}
				// 短暂休眠，避免出现活锁
				Thread.sleep(3, RANDOM.nextInt(500));
			}
		} catch (Exception e) {
			throw new RuntimeException("Locking error", e);
		} finally {
			jedis.close();
		}
		return false;
	}
	
	public boolean lock(long timeout, int expire, JedisPool sentinelPool,int dbNo) {
		Jedis jedis = sentinelPool.getResource();
		jedis.select(dbNo);
		long nano = System.nanoTime();
		timeout *= MILLI_NANO_CONVERSION;
		try {
			while ((System.nanoTime() - nano) < timeout) {
				if (jedis.setnx(this.key, LOCKED) == 1) {
					jedis.expire(this.key, expire);
					this.locked = true;
					return this.locked;
				}
				// 短暂休眠，避免出现活锁
				Thread.sleep(3, RANDOM.nextInt(500));
			}
		} catch (Exception e) {
			throw new RuntimeException("Locking error", e);
		} finally {
			jedis.close();
		}
		return false;
	}


	/**
	 * 解锁
	 * 无论是否加锁成功，都需要调用unlock
	 * 应该以：
	 * lock();
	 * try {
	 * 		doSomething();
	 * } finally {
	 * 		unlock()；
	 * }
	 * 的方式调用
	 */
	public void unlock(JedisSentinelPool sentinelPool) {
		Jedis jedis = sentinelPool.getResource();
		try{
			if (this.locked) {
				jedis.del(this.key);
			}
		}finally{
			jedis.close();
		}
	}
	
	/**
	 * 描述: 复制上一个方法 新
	 * @author 发动机
	 * @param sentinelPool
	 */
	public void unlock(JedisPool sentinelPool,int dbNo) {
		Jedis jedis = sentinelPool.getResource();
		jedis.select(dbNo);
		try{
			if (this.locked) {
				jedis.del(this.key);
			}
		}finally{
			jedis.close();
		}
	}
}
